package ua.kpi.oblamskyiv.parser;

import ua.kpi.oblamskyiv.factory.QuestionFactory;
import ua.kpi.oblamskyiv.testcase.Answers;
import ua.kpi.oblamskyiv.testcase.Questions;
import ua.kpi.oblamskyiv.testcase.answer.Answer;
import ua.kpi.oblamskyiv.testcase.question.Question;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parser for questions in string
 *
 * String format - sequence of:
 * <question type="" range="">
 *     <text></text>
 *     <answer></answer>
 *     <alternative></alternative>
 *     ...
 * </question>
 * separated by '\n'
 */
public class QuestionsParser {
    private String questionsString;

    public QuestionsParser(String questionsString) {
        this.questionsString = questionsString;
    }

    public Questions parseQuestions() {
        Scanner scanner = new Scanner(questionsString);
        List<Question> questions = new ArrayList<Question>();

        while(scanner.hasNextLine()) {
            String questionLine = scanner.nextLine();

            String questionType = parseType(questionLine);
            Double maxDeviation = null;
            if (questionType.equals("range")) {
                maxDeviation = parseDeviation(questionLine);
            }
            String textValue = parseTextValue(questionLine);
            Answers correctAnswers = parseAnswers(questionLine);
            Answers alternatives = parseAlternatives(questionLine);

            QuestionFactory.createQuestion(questionType, textValue,
                    correctAnswers, alternatives, maxDeviation);
        }
        return new Questions(questions);
    }

    private String parseTextValue(String string) {
        final String TEXT_VALUE =
                "(<text>)(.*?)(</text>)";
        final int STRING_GROUP_NUMBER = 2;

        return getFirstOccurrence(string, TEXT_VALUE, STRING_GROUP_NUMBER);
    }

    private String parseType(String string) {
        final String QUESTION_TYPE_PATTERN =
                "(<question type=\"(.*?)\")";
        final int QUESTION_TYPE_GROUP_NUMBER = 2;

        return getFirstOccurrence(string, QUESTION_TYPE_PATTERN, QUESTION_TYPE_GROUP_NUMBER);
    }

    private Answers parseAnswers(String string) {
        final String ANSWER_PATTERN =
                "(<answer>)(.*?)(</answer>)";
        final int ANSWER_TEXT_GROUP_NUMBER = 2;

        return getAnswersOccurrences(string,
                ANSWER_PATTERN, ANSWER_TEXT_GROUP_NUMBER);
    }

    private Answers parseAlternatives(String string) {
        final String ALTERNATIVE_PATTERN =
                "(<alternative>)(.*?)(</alternative>)";
        final int ALTERNATIVE_TEXT_GROUP_NUMBER = 2;

        return getAnswersOccurrences(string,
                ALTERNATIVE_PATTERN, ALTERNATIVE_TEXT_GROUP_NUMBER);
    }

    private double parseDeviation(String string) {
        final String DEVIATION_PATTERN =
                "(<question type=\"(.*?)\" (deviation=\"(.*?)\")?>)";
        final int DEVIATION_GROUP_NUMBER = 4;

        String deviation = getFirstOccurrence(string,
                DEVIATION_PATTERN, DEVIATION_GROUP_NUMBER);
        return Double.parseDouble(deviation);
    }

    private String getFirstOccurrence(String string,
                                      String stringPattern, int groupNumber) {
        Pattern pattern = Pattern.compile(stringPattern);
        Matcher matcher = pattern.matcher(string);

        if (!matcher.find())
            throw new IllegalArgumentException("Illegal string!");

        return matcher.group(groupNumber);
    }

    private Answers getAnswersOccurrences(String string,
                                          String stringPattern, int groupNumber) {
        Pattern pattern = Pattern.compile(stringPattern);
        Matcher matcher = pattern.matcher(string);

        List<Answer> answers = new ArrayList<Answer>();
        while (matcher.find()) {
            String answerText = matcher.group(groupNumber);
            answers.add(new Answer(answerText));
        }
        return new Answers(answers);
    }
}
